#include "ini.h"
#include "windows.h"
#include <stdio.h>
#include <string.h>
#include <direct.h>

///////////////////////////////////////////////////////////////////////////////

// #define FILE_TRACE

#include "file_trace.h"

///////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------------
// defines
//------------------------------------------------------------------------------

#define  INI_DELIMITER        ","

//------------------------------------------------------------------------------
// enums
//------------------------------------------------------------------------------

enum
{
    LINE_SIZE = 128,
    MAX_URL_BUFFER_LENGTH = 5 * 1024,
};

//------------------------------------------------------------------------------
// static variables
//------------------------------------------------------------------------------

// paths
static char s_ini_path[MAX_PATH_LENGTH] = {0};
static char s_data_path[MAX_PATH_LENGTH] = {0};

// feeds
static int s_feeds_count = 0;
static char* s_buffer_feeds = NULL;
static char** s_feeds = NULL;

// customers
static int s_customer_count = 0;
static char* s_buffer_customers = NULL;
static customer_t* s_customers = NULL;

// task schedule
static taskschedule_t s_task_schedule = {0};

//------------------------------------------------------------------------------
// static functions
//------------------------------------------------------------------------------

static bool
s_SetPaths(const char* a_ini_path);

static digest_opt_t
s_ConvertDigestOption(const char* a_digest_opt);

static format_opt_t
s_ConvertFormatOption(const char* a_format_opt);

char* 
s_trim(char* a_string);

//------------------------------------------------------------------------------
// public functions
//------------------------------------------------------------------------------

bool
INI_Init(const char* a_ini_path)
{
	char  task_schedule[MAX_URL_BUFFER_LENGTH] = {0};
    char**  keywords = NULL;
    char*  buffer = NULL;
    char*  temp = NULL;
    char*  token = NULL;
    int  keywordCount = 0;
    int  charCount = 0;
    int  i = 0;
    int  k = 0;
    int  j = 0;
    int  len = 0;
    bool  result = false;

	// sets all the paths based on the ini file path
	if (s_SetPaths(a_ini_path) == false)
	{
        goto exit;	
	}

    // try to allocate memory for the feeds to be read from the ini file
    s_buffer_feeds = (char*) malloc(sizeof(char) * MAX_URL_BUFFER_LENGTH);
    if (s_buffer_feeds == NULL)
    {
        F_ERROR(("malloc() failed!\n"));

        goto exit;
    }

    // reset
    memset(s_buffer_feeds, 0, sizeof(char) * MAX_URL_BUFFER_LENGTH);

    // read the feeds from the ini file
    charCount = GetPrivateProfileString("feeds", NULL, NULL, s_buffer_feeds, MAX_URL_BUFFER_LENGTH, s_ini_path);  
	if (charCount <= 0)
    {
        F_ERROR(("GetPrivateProfileString() failed!\n"));

        goto exit;
    }

    // how many urls?
    for (buffer = s_buffer_feeds, i = 0; i < charCount; buffer += len, s_feeds_count++, i += len)
    {
        len = strlen(buffer) + 1; // pass the null char!
    }

    // try to allocate memory for the feeds: note that they point to inside s_buffer_feeds memory
    s_feeds = (char**) malloc(sizeof(char*) * (s_feeds_count));
    if (s_feeds == NULL)
    {
        F_ERROR(("malloc() failed!\n"));

        goto exit;
    }

    // reset
    memset(s_feeds, 0, sizeof(char*) * s_feeds_count);

    // extract the lines
    for (buffer = s_buffer_feeds, i = 0; i < s_feeds_count; buffer += len + 1, i++)
    {
        len = strlen(buffer);
        s_feeds[i] = buffer;
    }

    // try to allocate memory for the customers to be read from the ini file
    s_buffer_customers = (char*) malloc(sizeof(char) * MAX_URL_BUFFER_LENGTH);
    if (s_buffer_customers == NULL)
    {
        F_ERROR(("malloc() failed!\n"));

        goto exit;
    }

    // reset
    memset(s_buffer_customers, 0, sizeof(char) * MAX_URL_BUFFER_LENGTH);

    // read the customers from the ini file
    charCount = GetPrivateProfileString("customers", NULL, NULL, s_buffer_customers, MAX_URL_BUFFER_LENGTH, s_ini_path);
    if (charCount <= 0)
    {
        F_ERROR(("GetPrivateProfileString() failed!\n"));

        goto exit;
    }

    // how many customers?
    for (buffer = s_buffer_customers, i = 0; i < charCount; buffer += len, s_customer_count++, i += len)
    {
        len = strlen(buffer) + 1; // pass the null char!
    }

    // try to allocate memory for the customers: note that they point to inside s_buffer_customers memory
    s_customers = (customer_t*) malloc(sizeof(customer_t) * s_customer_count);
    if (s_customers == NULL)
    {
        F_ERROR(("malloc() failed!\n"));

        goto exit;
    }

    // reset
    memset(s_customers, 0, sizeof(customer_t) * s_customer_count);

    // extract customers
    for (buffer = s_buffer_customers, i = 0; i < s_customer_count; buffer += len, i++)
    {
        len = strlen(buffer) + 1; // pass the null char!
        s_customers[i].name = s_trim(strtok(buffer, INI_DELIMITER));
        s_customers[i].surname = s_trim(strtok(NULL, INI_DELIMITER));
        s_customers[i].email = s_trim(strtok(NULL, INI_DELIMITER));
		s_customers[i].digest_opt = s_ConvertDigestOption(s_trim(strtok(NULL, INI_DELIMITER)));
		s_customers[i].format_opt = s_ConvertFormatOption(s_trim(strtok(NULL, INI_DELIMITER)));
        s_customers[i].time_zone = s_trim(strtok(NULL, INI_DELIMITER));

        // get the number of keywords
        keywordCount = 0;
        temp = s_customers[i].time_zone + strlen(s_customers[i].time_zone) + 2;
        while (temp = strstr(temp, INI_DELIMITER))
        {
            temp += 2;
            keywordCount++;
        }

        // 2: 1 for the last keyword and 1 for the NULL flag!
        keywordCount += 2;
        
        F_PRINT(("keyword count:|%d|\n", keywordCount));        

        // try to allocate memory for the keywords
        keywords = (char**) malloc(sizeof(char*) * (keywordCount));
        if (keywords == NULL)
        {
            F_ERROR(("malloc() failed!\n"));

            goto exit;
        }

        // reset
        memset(keywords, 0, sizeof(char*) * (keywordCount));

        // one-by-one extract the keywords
        for (j = 0; j < keywordCount; j++)
        {
            keywords[j] = s_trim(strtok(NULL, INI_DELIMITER));
        }

        // finally assign the keywords
        s_customers[i].keywords = keywords;
    }

	// read the task schedule parameters from the ini file
	charCount = GetPrivateProfileString("taskschedule", NULL, NULL, task_schedule, MAX_URL_BUFFER_LENGTH, s_ini_path);
	if (charCount <= 0)
	{
		F_ERROR(("GetPrivateProfileString() failed!\n"));

		goto exit;
	}
	
	// set the task schedule options
	strncpy(s_task_schedule.name, s_trim(strtok(task_schedule, INI_DELIMITER)), TASK_NAME_LENGTH);
	strncpy(s_task_schedule.period, s_trim(strtok(NULL, INI_DELIMITER)), TASK_PERIOD_LENGTH);
	s_task_schedule.ini_path = s_ini_path;

	result = true;

exit:

    return result;
}

const char*
INI_GetIniPath()
{
	return s_ini_path;
}

const char*
INI_GetDataPath()
{
	return s_data_path;
}

const char**
INI_FeedSources()
{
    return s_feeds;
}

int
INI_FeedCount()
{
    return s_feeds_count;
}

int
INI_CustomerCount()
{
    return s_customer_count;
}

customer_t*
INI_Customers()
{
    return s_customers;
}

taskschedule_t*
INI_TaskSchedule()
{
	return &s_task_schedule;
}

void
INI_Free()
{
    int  customer;
	int  keyword;

    // no memory leak!

    // first, one-by-free customer digest and keywords memory
	// note raw_digest is a malloc'ed array of arrays!
    for (customer = 0; customer < s_customer_count; customer++)
    {
        for (keyword = 0; s_customers[customer].keywords[keyword] != NULL; keyword++)
        {
			if (s_customers[customer].raw_digest && s_customers[customer].raw_digest[keyword])
			{
				free(s_customers[customer].raw_digest[keyword]);
				s_customers[customer].raw_digest[keyword] = NULL;
			}
		}

        free(s_customers[customer].raw_digest);
        s_customers[customer].raw_digest = NULL;
        
        free(s_customers[customer].formatted_digest);
        s_customers[customer].formatted_digest = NULL;

        free(s_customers[customer].keywords);
        s_customers[customer].keywords = NULL;
    }

    // second, free the other allocated memory
    free(s_buffer_feeds);
    free(s_buffer_customers);
    free(s_feeds);
    free(s_customers);

    s_buffer_feeds = NULL;
    s_buffer_customers = NULL;
    s_feeds = NULL;
    s_customers = NULL;

	// reset
    s_customer_count = 0;
	s_feeds_count = 0;
	memset(s_ini_path, 0, MAX_PATH_LENGTH);
	memset(s_data_path, 0, MAX_PATH_LENGTH);
	memset(&s_task_schedule, 0, sizeof(taskschedule_t));
}

//------------------------------------------------------------------------------
// static functions
//------------------------------------------------------------------------------

static bool
s_SetPaths(const char* a_ini_path)
{
	const char*  data_path = s_ini_path;
	int  i = 0;

	// is the ini path specified?
	if (a_ini_path == NULL)
	{
		// no: get the current path
		if (GetCurrentDirectory(MAX_PATH_LENGTH - FILE_NAME_LENGTH, s_ini_path) == 0)
		{
			F_ERROR(("GetCurrentDirectory() failed...\n"));

			return false;
		}

		// append the ini file name with a slash
		strcat(s_ini_path, "\\");
		strcat(s_ini_path, INI_FILE_NAME);
	}
	else
	{
		// yes: check its length
		if (strlen(a_ini_path) > MAX_PATH_LENGTH)
		{
			F_ERROR(("ini path |%s| is longer than |%d|...\n", a_ini_path, MAX_PATH_LENGTH));

			return false;
		}

		// set the ini path
		strcpy(s_ini_path, a_ini_path);
	}

	// set the data path
	// unfortunately Microsoft doesn't support strrstr()
	data_path += strlen(s_ini_path); // goto to the end!
	while (true)
	{
		if (*(--data_path) == '\\')
			break;
	}

	strncpy(s_data_path, s_ini_path, data_path - s_ini_path + 1);
	strcat(s_data_path, DATA_DIR);

	F_PRINT(("\n"
		     "ini path:|%s|\n"
			 "data path:|%s|\n",
			 s_ini_path,
			 s_data_path));

	return true;
}

static digest_opt_t
s_ConvertDigestOption(const char* a_digest_opt)
{
	// if the option specified is not known, return "title+desc" option
   if (strcmp(a_digest_opt, "Digest:Title") == 0)	
   {	
	   return digest_opt_title;
   }
   else
   {
       return digest_opt_title_desc;
   }
}

static format_opt_t
s_ConvertFormatOption(const char* a_format_opt)
{
	// if the option specified is not known, return "html" format
   if (strcmp(a_format_opt, "Format:Text") == 0)	
   {	
	   return format_opt_text;
   }
   else
   {
       return format_opt_html;
   }
}

char* 
s_trim(char* a_string)
{
  char* end = NULL;

  if (a_string == NULL)
      goto exit;

  // Trim leading space
  while(isspace(*a_string)) a_string++;

exit:

  return a_string;
}
